5 Note 1: Make sure to add in the python Path manager the following path:
6 ...\Python_Scripts\Experiments\MNREAD\MNREAD_VR_Experiment\Algorithms
7 containing the different libraries needed for this main code
8 Note 2: When you open the .csv results file (say with the opensource LibreOffice),
9 make sure to solely use the separator ";" (i.e. the semi-colon character)
12 goal : This experiment recreates the MNRead test in VR
14 how to use : Execute this script to launch the entire experiment.
15 The main parameters of the experiment have to be set in the file mnread_vr_parameters.py
17 predictions : An interface allowing you to start the experiment should be displayed on
19 For each scene, a new sentence is displayed in the headset. An interface should be
20 visible on the PC screen to interact with the experiment
22 tech note : https://notes.inria.fr/qJt3btPVTnS5zXckWjzn2Q
24 Created on : Tue May 17 11:22:36 2022
38 import mnread_vr_parameters
as parameters
39 import mnread_vr_interface
as interface
40 import mnread_vr_interactions
as interactions
50 headPOV = parameters.height_of_subject_in_m
52 current_coordinate_system = np.array([0, headPOV, 0])
53 informations_text_size = parameters.visual_angle_of_centered_object
56 current_tangent_screen = []
58 new_origin_coordinates = []
65 if (parameters.scotoma_visible):
67 contingent_type=ReticleContingency.GAZE)
68 scotoma.sprite = parameters.scotoma_image
69 scotoma.spriteAngularRadiusX = parameters.scotoma_x_angular_radius
70 scotoma.spriteAngularRadiusY = parameters.scotoma_y_angular_radius
71 scene.AddPointingCursor(scotoma)
73 if (parameters.reticle_visible):
74 reticle_gaze_color = color.RGBColor(r=0, g=0, b=0.9)
75 my_reticle_outer_radius_deg = 6.
76 my_reticle_inner_radius_deg = 2.
79 my_image_resolution_pix = 512
82 my_reticle_color = reticle_gaze_color
83 my_reticle_line_width_deg = 0.5
85 reticle_gen = RG.ReticleImageFromDrawing(image_resolution_pix=my_image_resolution_pix,
86 reticle_outer_radius_deg=my_reticle_outer_radius_deg,
87 reticle_inner_radius_deg=my_reticle_inner_radius_deg,
88 reticle_color=my_reticle_color,
89 reticle_line_width_deg=my_reticle_line_width_deg)
92 contingent_type=ReticleContingency.GAZE)
93 reticle_eyes.SetMaxDistanceProjectionOptions(
94 max_distance=parameters.viewing_distance_in_m, is_projected=
False)
95 reticle_gen.Initialize(new_image_resolution_pix=my_image_resolution_pix, new_reticle_inner_radius=my_reticle_inner_radius_deg,
96 newreticle_outer_radius_deg=my_reticle_outer_radius_deg, isDefaultReticle=
True, reticle_color=reticle_gaze_color, reticle_line_width_deg=my_reticle_line_width_deg)
97 reticle_eyes.SetCustomReticleGenerator(reticle_gen)
98 scene.AddPointingCursor(reticle_eyes)
103 screen_width = 18 * tools.visual_angle_to_size_on_perpendicular_plane(visual_angle_of_centered_object_in_deg=visual_angle_of_centered_object,
104 viewing_distance_in_m=parameters.viewing_distance_in_m)
106 screen_height = 8 * tools.visual_angle_to_size_on_perpendicular_plane(visual_angle_of_centered_object_in_deg=visual_angle_of_centered_object,
107 viewing_distance_in_m=parameters.viewing_distance_in_m)
109 if (is_active
and parameters.record_eye_gaze
and first_screen ==
False):
110 tangent_screen =
TangentScreen(position_in_current_CS=np.array([0, 0, (parameters.viewing_distance_in_m)]),
111 color=parameters.tangent_screen_color,
112 size_in_meters=np.array(
113 [screen_width, screen_height]),
117 tangent_screen =
TangentScreen(position_in_current_CS=np.array([0, 0, (parameters.viewing_distance_in_m)]),
118 color=parameters.tangent_screen_color,
119 size_in_meters=np.array(
120 [screen_width, screen_height]),
124 scene.fill_in_results_file_column(
125 column_name=parameters.resultfile_tangent_screen_width_in_m, value=screen_width)
126 scene.fill_in_results_file_column(
127 column_name=parameters.resultfile_tangent_screen_height_in_m, value=screen_height)
128 return tangent_screen
132 visual_angle_of_centered_object,
133 scene, my_world, phrase_to_write="", tag="",
136 This function is used to create each scene displayed
143 visual_angle_of_centered_object=visual_angle_of_centered_object, scene=scene, is_active=
True)
145 x_height_in_meters = tools.visual_angle_to_size_on_perpendicular_plane(visual_angle_of_centered_object_in_deg=visual_angle_of_centered_object,
146 viewing_distance_in_m=parameters.viewing_distance_in_m)
149 text =
Text(text=phrase_to_display,
150 font_name=parameters.font_to_use,
151 color=parameters.text_color,
152 horizontal_alignment=
"Flush",
153 vertical_alignment=
"Middle",
154 visual_angle_of_centered_x_height_deg=visual_angle_of_centered_object,
157 text.isWrapping =
True
158 text.textboxScale = np.array([(17.32 * x_height_in_meters), 1.0])
165 scene.fill_in_results_file_column(
166 column_name=parameters.resultfile_sentence_display, value=phrase_to_write)
167 scene.fill_in_results_file_column(
168 column_name=parameters.resultfile_sentence_tag, value=tag)
169 scene.fill_in_results_file_column(
170 column_name=parameters.resultfile_visual_angle, value=str(visual_angle_of_centered_object))
171 scene.fill_in_results_file_column(
172 column_name=parameters.resultfile_misread_words, value=
"")
173 scene.fill_in_results_file_column(
174 column_name=parameters.resultfile_logmar, value=str(logmar_value))
175 scene.fill_in_results_file_column(
176 column_name=parameters.resultfile_viewing_distance, value=parameters.viewing_distance_in_m)
177 scene.fill_in_results_file_column(
178 column_name=parameters.resultfile_sentence_number, value=interactions.counter_sentences)
179 scene.fill_in_results_file_column(
180 column_name=parameters.resultfile_tangent_screen_id, value=tangent_screen.id)
184 next_visual_angle = tools.logmar_to_visual_angle_in_degrees(
185 logmar_value - parameters.decreasing_size)
186 if (parameters.sentences_testing_mode):
188 visual_angle_of_centered_object=visual_angle_of_centered_object, scene=scene, is_active=
False)
191 visual_angle_of_centered_object=next_visual_angle, scene=scene, is_active=
False)
197 text.set_perimetric_coordinates_on_screen(tangent_screen,
198 eccentricity_local_deg=0.0,
199 half_meridian_local_deg=0.0
201 scene.place(tangent_screen, my_world)
202 scene.place(empty_tangent_screen, my_world)
203 scene.place(text, my_world)
205 current_tangent_screen.append(tangent_screen)
206 empty_screen.append(empty_tangent_screen)
208 my_world.add_scene(scene)
213 At each scene a new sentence is displayed. Each new phrase has a decreasing size.
220 current_visual_angle = parameters.visual_angle_of_centered_object
222 if (parameters.start_logmar):
223 current_visual_angle = tools.logmar_to_visual_angle_in_degrees(
224 parameters.start_logmar)
226 for i
in range(parameters.number_of_sentences_to_use):
227 interactions.counter_sentences += 1
228 phrase_to_display = parameters.phrases_to_display[i].rstrip(
"\n")
230 if (parameters.use_tagged_sentences):
231 sentence_tag = parameters.sentence_tag[i]
233 logmar_value = tools.visual_angle_in_degrees_to_logmar(
234 current_visual_angle)
236 list_cutted_phrases_from_mnread_phrase_cutting = mnread.mnread_phrase_cutting(
239 phrase_with_back_to_line = str(list_cutted_phrases_from_mnread_phrase_cutting[0]) +
"\n" + str(
240 list_cutted_phrases_from_mnread_phrase_cutting[1]) +
"\n" + str(list_cutted_phrases_from_mnread_phrase_cutting[2])
243 t1 =
VisualScene(background_color=parameters.scene_background_color)
245 create_scene(phrase_to_display=phrase_with_back_to_line,
246 visual_angle_of_centered_object=current_visual_angle,
249 phrase_to_write=phrase_to_display,
251 logmar_value=logmar_value)
254 interface.create_background_image_UI(t1)
256 interactions.create_words_UI(
257 t1, my_world, list_cutted_phrases_from_mnread_phrase_cutting)
259 interactions.create_main_UI(t1, my_world, list_cutted_phrases_from_mnread_phrase_cutting,
260 current_tangent_screen[0], empty_screen[0], logmar_value, current_visual_angle)
261 current_tangent_screen.clear()
264 if (i == len(parameters.phrases_to_display)):
265 smallest_logmar = logmar_value
268 if (parameters.sentences_testing_mode ==
False):
269 current_visual_angle = tools.logmar_to_visual_angle_in_degrees(
270 logmar_value - parameters.decreasing_size)
275 interface.create_background_image_UI(end_scene)
276 interactions.end_experiment(my_world, end_scene, smallest_logmar)
277 my_world.add_scene(end_scene)
284 operatorMode=
True, name_of_subject=parameters.subject_name)
285 my_world.translate_coordinate_system_along_global(
286 current_coordinate_system)
288 background_color=parameters.scene_background_color)
289 my_world.add_scene(start_scene)
291 current_visual_angle = parameters.visual_angle_of_centered_object
292 if (parameters.start_logmar):
293 current_visual_angle = tools.logmar_to_visual_angle_in_degrees(
294 parameters.start_logmar)
297 current_visual_angle, start_scene, is_active=
True, first_screen=
True)
298 start_scene.place(first_empty_screen, my_world)
302 interactions.start_experiment(start_scene, my_world, first_empty_screen)
305 for i
in range(1, parameters.number_of_sentences_to_use):
306 my_world.scenes[my_world.id_scene[i]].interaction[-1].callbacks[0].SetNextScene(
307 id_next_scene=my_world.id_scene[-1])
309 print(
"The experiment has been written.")
312 if __name__ ==
"__main__":
def LaunchThe3DWorld(jsonFileCategory="Externals")
def create_scotoma(scene)
def create_scene(phrase_to_display, visual_angle_of_centered_object, scene, my_world, phrase_to_write="", tag="", logmar_value=0)
def create_tangent_screen(visual_angle_of_centered_object, scene, is_active, first_screen=False)
def sentences_sequence_mnread(my_world)